//
// Created by Administrator on 2023/9/1.
//
#include <stdio.h>
#include <math.h>
#include "oled.h"
#include "../Middlewares/u8g2/csrc/u8g2.h"
#include "debug.h"
#include "cmsis_os2.h"
#include "FreeRTOS.h"

static u8g2_t u8g2;

// 因为开发板的结构导致用硬件spi就需要用杜邦线，很难看还不方便，如果用软件spi，就可以直接把屏幕插在开发板的排针上，十分优雅
// 如果用esp32开发板，硬件spi可以复用到任何一个io口上，

// 这里的这个软件spi是u8g2自带的，不推荐使用(速度超级慢)，选择硬件spi，自己手动模拟spi的发送函数也要比这个快很多，
// 这里留着的原因是为了对比与硬件spi（自己模拟的）速度做对比
#define CONFIG_SW_SPI_U8G2  (0)
#define CONFIG_HW_SPI_U8G2  (1)


//       RAM:       12224 B        64 KB     18.65%
// 英文 FLASH:       55780 B       512 KB     10.64%
// 中文 FLASH:      258452 B       512 KB     49.30%
#define ENGLISH 0 //1英文 0中文

#if CONFIG_SW_SPI_U8G2
static uint8_t u8g2_gpio_and_delay_stm32(U8X8_UNUSED u8x8_t *u8x8, U8X8_UNUSED uint8_t msg, U8X8_UNUSED uint8_t arg_int,
                                  U8X8_UNUSED void *arg_ptr) {
    switch (msg) {
        //Initialize SPI peripheral
        case U8X8_MSG_GPIO_AND_DELAY_INIT:
            break;
            //Function which implements a delay, arg_int contains the amount of ms
        case U8X8_MSG_DELAY_MILLI:
            HAL_Delay(arg_int);
            break;
            //Function which delays 10us
        case U8X8_MSG_DELAY_10MICRO:
            break;
            //Function which delays 100ns
        case U8X8_MSG_DELAY_100NANO:
            break;
            //Function to define the logic level of the clockline
        case U8X8_MSG_GPIO_SPI_CLOCK:
            if (arg_int) OLED_CLK_Set();
            else OLED_CLK_Clr();
            break;
            //Function to define the logic level of the data line to the display
        case U8X8_MSG_GPIO_SPI_DATA:
            if (arg_int) OLED_SDA_Set();
            else OLED_SDA_Clr();
            break;
            // Function to define the logic level of the CS line
        case U8X8_MSG_GPIO_CS:
            if (arg_int) OLED_CS_Set();
            else OLED_CS_Clr();
            break;
            //Function to define the logic level of the Data/ Command line
        case U8X8_MSG_GPIO_DC:
            if (arg_int) OLED_DC_Set();
            else OLED_DC_Clr();
            break;
            //Function to define the logic level of the RESET line
        case U8X8_MSG_GPIO_RESET:
            if (arg_int) OLED_RST_Set();
            else OLED_RST_Clr();
            break;
        default:
            return 0; //A message was received which is not implemented, return 0 to indicate an error
    }
    return 1; // command processed successfully.
}
#endif

#if CONFIG_HW_SPI_U8G2

/*
* 函数名：uint8_t mySPI_ReadWriteByte(uint8_t TxData)
* 输入参数：TxData 待写入的数据
* 返回值：读取到的数据
* 函数作用：模拟SPI读写数据
*/
static uint8_t SpiReadWriteByte(uint8_t TxData)
{
    uint8_t bit_ctr, RxData = 0x00;

    for(bit_ctr=0;bit_ctr<8;bit_ctr++)
    {
        if(TxData & 0x80) OLED_SDA_Set();
        else OLED_SDA_Clr();
        TxData <<= 1;
        OLED_CLK_Set();
//        if(READ_MISO())   RxData |= (0x80 >> bit_ctr);//驱动屏幕没用到读取函数
        OLED_CLK_Clr();
    }
    return(RxData);
}

static uint8_t u8x8_byte_3wire_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
    uint8_t *p = (uint8_t*)arg_ptr;
    switch (msg)
    {
        /*通过SPI发送arg_int个字节数据*/
        case U8X8_MSG_BYTE_SEND:
            OLED_CS_Clr();
            for(int i = 0; i < arg_int; i++) SpiReadWriteByte((uint8_t)*(p + i));
            OLED_CS_Set();
            break;

            /*设置DC引脚，DC引脚控制发送的是数据还是命令*/
        case U8X8_MSG_BYTE_SET_DC:
            if (arg_int) OLED_DC_Set();
            else OLED_DC_Clr();
            break;

            /*初始化函数*/
        case U8X8_MSG_BYTE_INIT:
            break;

            /* 下面功能无需定义 */
            /*开始传输前会进行的操作，如果使用软件片选可以在这里进行控制*/
        case U8X8_MSG_BYTE_START_TRANSFER:
            break;

            /*传输后进行的操作，如果使用软件片选可以在这里进行控制*/
        case U8X8_MSG_BYTE_END_TRANSFER:
            break;
        default:
            return 0;
    }
    return 1;
}

static uint8_t u8g2_gpio_and_delay_stm32(U8X8_UNUSED u8x8_t *u8x8,
                                  U8X8_UNUSED uint8_t msg, U8X8_UNUSED uint8_t arg_int,
                                  U8X8_UNUSED void *arg_ptr)
{
    return 1;
}
#endif

void U8G2_Init(void) {

#if CONFIG_SW_SPI_U8G2
    u8g2_Setup_ssd1306_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_4wire_sw_spi, u8g2_gpio_and_delay_stm32);
#endif
#if CONFIG_HW_SPI_U8G2
    u8g2_Setup_ssd1306_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_3wire_hw_spi, u8g2_gpio_and_delay_stm32);
#endif
    u8g2_InitDisplay(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);
    u8g2_ClearDisplay(&u8g2);

#if ENGLISH
    u8g2_SetFont(&u8g2,u8g2_font_7x14_mn);
#else
    u8g2_SetFont(&u8g2,u8g2_font_wqy12_t_gb2312);
#endif

//    u8g2_DrawStr(&u8g2,0,16,"STM32 U8g2");
//    u8g2_DrawUTF8(&u8g2,0,32,"测试中文");
//    u8g2_SendBuffer(&u8g2);
}

#if 1
//==================================================
typedef struct {
    int firstshow;	//第一行显示的位置
    int nowmouse;	//光标显示的位置
    int nowindex;	//索引
}MENU_DATA_STRUCT;

MENU_DATA_STRUCT menu1;

typedef struct
{
    char* str;
}SETRING_LIST;

SETRING_LIST list[] =
{
#if ENGLISH
    {"11"},
    {"222222"},
    {"3333333333333333333"},
    {"4444444"},
    {"55555"},
    {"6666666666"},
    {"777777777"},
    {"88888"},
    {"99999"},
#else
    {"驯鹿的响铃"},
    {"和雪花的相遇"},
    {"当作是我对你离开"},
    {"描绘的旋律"},
    {"柴火写了序"},
    {"暖和童话的过去"},
    {"远方的烟囱冒着烟"},
    {"该不会是我的卡片"},
    {"那是我让圣诞老人"},
    {"捎去的想念"},
#endif
};

#define MAX_LINE (4)
//#define LIST_LEN (10)
const uint8_t LIST_LEN = ( sizeof(list) / sizeof(list[0]) );

void ui_run(int *a, int *a_trg)
{
    if(*a == *a_trg) return;
    if(*a == *a_trg - 1) *a = *a_trg;
    *a = (*a + *a_trg)>>1;
}

/* u8g2_cursor：光标移动函数
 * x: 起始位置横坐标
 * y：起始位置纵坐标
 * s: 要显示的字符串
 * return false 没有运行结束，还需要接着运行
 * return true  运行结束
 * */
bool u8g2_cursor(int x, int y, char * s)
{
#if 0
    static int y_m = -16;
    static int w_m = 0;

    int w = u8g2_GetStrWidth(&u8g2, s);
    int h = u8g2_GetAscent(&u8g2)-u8g2_GetDescent(&u8g2);

    ui_run(&y_m, &y);
    ui_run(&w_m, &w);

    u8g2_SetFontPosBaseline(&u8g2);
    u8g2_DrawRFrame(&u8g2, x-2, y_m-u8g2_GetAscent(&u8g2)-2, w_m+4, h+3, 3);

    return (w_m == w && y_m == y);
#else
    static int y_m = 0;
    static int w_m = 0;

#if ENGLISH
    int w = u8g2_GetStrWidth(&u8g2, s); //宽度
    int h = u8g2_GetAscent(&u8g2)-u8g2_GetDescent(&u8g2)-2; //高度
#else
    int w = u8g2_GetUTF8Width(&u8g2, s); //宽度
    int h = u8g2_GetAscent(&u8g2)-u8g2_GetDescent(&u8g2)+2; //高度
#endif

    ui_run(&y_m, &y);
    ui_run(&w_m, &w);

//    u8g2_SetFontPosBaseline(&u8g2);//更改字形和字符串绘制的参考位置 功能
//    u8g2_DrawRFrame(&u8g2, x-2, y_m-u8g2_GetAscent(&u8g2)-2, w_m+4, h+3, 3);

    u8g2_SetFontMode(&u8g2,1); //定义字形和字符串绘制函数是否 写入背景颜色（模式 0/纯色、） 或 not （模式 1/透明， ）默认模式为 0
    u8g2_SetDrawColor(&u8g2,1);;//定义所有绘图函数的位值（颜色索引）。 所有绘图功能都会将显示内存更改为此位值。默认值为 1

#if ENGLISH
    u8g2_DrawRBox(&u8g2, x-3, y_m-h, w_m+6, h, 3);
#else
    u8g2_DrawRBox(&u8g2, x-3, y_m-h, w_m+6, h, 3);
#endif

    u8g2_SetFontMode(&u8g2,0); //定义字形和字符串绘制函数是否 写入背景颜色（模式 0/纯色、） 或 not （模式 1/透明， ）默认模式为 0
    u8g2_SetDrawColor(&u8g2,1);;//定义所有绘图函数的位值（颜色索引）。 所有绘图功能都会将显示内存更改为此位值。默认值为 1

    return (w_m == w && y_m == y);
#endif
}
/*
* u8g2_content 主界面移动
* return false 没有运行结束，还需要接着运行
* return true  运行结束
* */
bool u8g2_content(int x, int index)
{
    static int y_m = 16;

    int y = (index+1)*16;

//    DBG("%d --> %d\n", y_m, y);

    ui_run(&y_m, &y);

    u8g2_SetFontMode(&u8g2,1); //定义字形和字符串绘制函数是否 写入背景颜色（模式 0/纯色、） 或 not （模式 1/透明， ）默认模式为 0
    u8g2_SetDrawColor(&u8g2,2);;//定义所有绘图函数的位值（颜色索引）。 所有绘图功能都会将显示内存更改为此位值。默认值为 1
    for(int i = 0 ;i < LIST_LEN; i++)
    {
#if ENGLISH
        u8g2_DrawStr(&u8g2, x, y_m+i*16-1, list[i].str);
#else
        u8g2_DrawUTF8(&u8g2, x, y_m+i*16-1, list[i].str);
#endif

    }
    u8g2_SetFontMode(&u8g2,0); //定义字形和字符串绘制函数是否 写入背景颜色（模式 0/纯色、） 或 not （模式 1/透明， ）默认模式为 0
    u8g2_SetDrawColor(&u8g2,1);;//定义所有绘图函数的位值（颜色索引）。 所有绘图功能都会将显示内存更改为此位值。默认值为 1

    return (y_m == y);
}
/*
 * return false 没有运行结束，还需要接着运行
 * return true  运行结束
 * */
bool ui_show(uint16_t key)
{
    bool ret = false;
    bool ret1 = false;

    switch(key)
    {
        case 0: break;
        case KEY1_Pin:
            if(menu1.nowindex < LIST_LEN-1)menu1.nowindex++;

            if(menu1.nowmouse < MAX_LINE-1){
                menu1.nowmouse++;
            } else {
                if (menu1.firstshow < LIST_LEN - MAX_LINE)menu1.firstshow++;
            }
        break;
        case KEY2_Pin:
            if(menu1.nowindex > 0)menu1.nowindex--;

            if(menu1.nowmouse > 0){
                menu1.nowmouse--;
            } else {
                if (menu1.firstshow > 0)menu1.firstshow--;
            }
        break;
        default: break;
    }

//    DBG("Index = %d first = %d Mouse = %d\n", menu1.nowindex, menu1.firstshow, menu1.nowmouse);

    u8g2_ClearBuffer(&u8g2);         // 清除内部缓冲区

    ret = u8g2_cursor(3, 16*(menu1.nowmouse+1), list[menu1.nowindex].str); //绘制光标
    ret1 = u8g2_content(3, 0-menu1.firstshow); //绘制字符

    u8g2_SendBuffer(&u8g2);          // 发送内部缓冲区

    return (ret && ret1);
}
#endif
#if 0
//屏幕像素向右边移动1列
void u8g2_right_move(void)
{
    uint8_t *p = u8g2_GetBufferPtr(&u8g2);

    //这里只能倒序移动，先把126移动到127 再把125移动到126 >>>>
    //移动到 1移动到2 的时候就要把0给清楚

    // 移动1-127
    for(int i = 1; i < u8g2.pixel_buf_width; i++)
    {
        //屏幕是128 * 64， 8个按一次 所以得移动8次
        for(int j = 1; j <= u8g2.tile_buf_height; j++)
        {
            *(p + (u8g2.pixel_buf_width) * j - i) = *(p + (u8g2.pixel_buf_width) * j - i - 1);
        }
    }

    //处理最后的0
    for(int j = 0; j < u8g2.tile_buf_height; j++)
    {
        *(p + (u8g2.pixel_buf_width) * j) = 0x00;
    }

    u8g2_SendBuffer(&u8g2);          // 发送内部缓冲区
}

//m控制周期，h控制幅度，绘制正弦波
static void grap(double m, int h)
{
    static double i = 0;
    static int y = 31, y1 = 31;

    i+=m;

    y = (int)(sin(i) * h + 31);

    u8g2_DrawLine(&u8g2, 0, y, 0, y1);
    u8g2_right_move();

    y1 = y;
}

void ui_test(uint16_t key)
{
#define NUMBER 2    //1 采集按键， 2 正弦波

#if (NUMBER == 1)
    /* 按KEY1 波形上下动*/
    static int y = 63, y1 = 63;

    if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 0)
    {
        y = 31-15;
    }
    else
    {
        y = 31+15;
    }

    u8g2_DrawLine(&u8g2, 0, y, 0, y1);
    u8g2_right_move();

    y1 = y;
#endif
#if (NUMBER == 2)

    static double m = 0.1;//控制周期
    static int h = 32;//控制幅度

    switch(key)
    {
        case KEY1_Pin:
            m += 0.02;
            break;
        case KEY2_Pin:
            m -= 0.02;
            break;
        case KEY3_Pin:
            if(h < 32) h += 4;
            break;
        case KEY4_Pin:
            if(h > 0) h -= 4;
            break;
        default: break;
    }

    grap(m, h);
#endif
}
#endif

#if 0
static const uint8_t setting[][16] = {
    /*"C:\Users\Administrator\Pictures\icon_设置.bmp",0*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07,0x00},
    {0x00,0x61,0x06,0x01,0xC0,0x73,0xCE,0x03,0xE0,0x3F,0xFC,0x07,0x60,0x1E,0x78,0x0E},
    {0x60,0x00,0x00,0x06,0xE0,0xE0,0x07,0x07,0xC0,0xF8,0x1F,0x03,0xC0,0x38,0x3C,0x07},
    {0x60,0x0C,0x30,0x06,0x7C,0x0E,0x70,0x3E,0x3C,0x06,0x60,0x3C,0x0C,0x06,0x60,0x30},
    {0x0C,0x06,0x60,0x30,0x7C,0x06,0x60,0x3C,0x7C,0x0E,0x60,0x3E,0xE0,0x0C,0x30,0x07},
    {0xC0,0x3C,0x3C,0x03,0xC0,0xF8,0x1F,0x03,0xE0,0xE0,0x07,0x07,0x60,0x00,0x00,0x0E},
    {0x60,0x1E,0x70,0x06,0xE0,0x7F,0xFC,0x07,0xC0,0x73,0xCE,0x03,0x00,0x60,0x06,0x00},
    {0x00,0xE0,0x07,0x00,0x00,0xE0,0x07,0x00,0x00,0x80,0x01,0x00,0x00,0x00,0x00,0x00},
};

static const uint8_t battery[][16] = {
    /*"C:\Users\Administrator\Pictures\电池电量.bmp",0*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0xF0,0xFF,0xFF,0xFF,0xFF,0x00,0xF8,0xFF,0xFF,0xFF,0xFF,0x03},
    {0xF8,0xFF,0xFF,0xFF,0xFF,0x03,0x18,0x00,0x00,0x00,0x80,0x03,0xD8,0xFF,0xFF,0x07},
    {0x80,0x03,0xD8,0xFF,0xFF,0x07,0x80,0x07,0xD8,0xFF,0xFF,0x07,0x80,0x1F,0xD8,0xFF},
    {0xFF,0x07,0x80,0x1F,0xD8,0xFF,0xFF,0x07,0x80,0x1F,0xD8,0xFF,0xFF,0x07,0x80,0x1F},
    {0xD8,0xFF,0xFF,0x07,0x80,0x1F,0xD8,0xFF,0xFF,0x07,0x80,0x1F,0xD8,0xFF,0xFF,0x07},
    {0x80,0x03,0xD8,0xFF,0xFF,0x07,0x80,0x03,0x18,0x00,0x00,0x00,0x80,0x03,0xF8,0xFF},
    {0xFF,0xFF,0xFF,0x03,0xF8,0xFF,0xFF,0xFF,0xFF,0x01,0xE0,0xFF,0xFF,0xFF,0x7F,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
};

//2024-01-07 add
void ui_test(uint16_t key)
{
    u8g2_SetDrawColor(&u8g2, 1);//1 正常显示 0 反显示
    u8g2_DrawXBM(&u8g2, 0,0,32,32, (uint8_t *)setting);
    u8g2_DrawXBM(&u8g2, 32,0,48,48, (uint8_t *)battery);
    u8g2_SendBuffer(&u8g2);          // 发送内部缓冲区
}

#endif